﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Practices.ServiceLocation;
using Pap.Core.Utilities;
using Pap.Data;
using Pap.Data.Abstraction.Utils;
using Pap.Services;
using Pap.Services.WorkflowEngine;
using Pap.Web.Utils;
using Utils;

namespace Pap.Web.Modules.Loan.ViewModel
{
    /// <summary>
    /// 
    /// </summary>
    public class CreateLoanViewModel : IViewModel
    {
        #region [ Fields ]

        /// <summary>
        /// 
        /// </summary>
        private readonly IAdministrationRepository _adminstrationRepository;
        /// <summary>
        /// 
        /// </summary>
        private readonly ILoanExecutionService _loanExecutionService;
        /// <summary>
        /// 
        /// </summary>
        private readonly ILoanRepository _loanRepository;
        /// <summary>
        /// 
        /// </summary>
        private readonly ILookUpRepository _lookupRepository;
        /// <summary>
        /// 
        /// </summary>
        private readonly IResponsibleService _responsibleService;
        /// <summary>
        /// 
        /// </summary>
        private readonly IWorkflowService _workFlowService;
        /// <summary>
        /// 
        /// </summary>
        private bool AgeExceeded
        {
            set;
            get;
        }


        #endregion

        #region [ Constructor ]

        /// <summary>
        /// Initializes a new instance of the <see cref="CreateLoanViewModel"/> class.
        /// </summary>
        /// <param name="loanRepo">The loan repo.</param>
        /// <param name="adminRepo">The admin repo.</param>
        /// <param name="repoService">The repo service.</param>
        /// <param name="workFlowService">The work flow service.</param>
        /// <param name="loanExecutionService">The loan execution service.</param>
        /// <param name="lookUpRepository">The look up repository.</param>
        public CreateLoanViewModel(ILoanRepository loanRepo, IAdministrationRepository adminRepo,
                                   IResponsibleService repoService,
                                   IWorkflowService workFlowService, ILoanExecutionService loanExecutionService,
                                   ILookUpRepository lookUpRepository)
        {
            _adminstrationRepository = adminRepo;
            _loanRepository = loanRepo;
            _responsibleService = repoService;
            _workFlowService = workFlowService;
            _loanExecutionService = loanExecutionService;
            _lookupRepository = lookUpRepository;
        }

        #endregion

        #region [ GetAll Lookups ]

        /// <summary>
        /// Gets all locations.
        /// </summary>
        /// <returns></returns>
        public IEnumerable<IIssuePlace> GetAllLocations()
        {
            return _loanRepository.FindIssuePlaces(ip => true);
        }

        /// <summary>
        /// Gets all governates.
        /// </summary>
        /// <returns></returns>
        public IEnumerable<IGovernorate> GetAllGovernates()
        {
            return _loanRepository.FindGovernates(g => true);
        }

        /// <summary>
        /// Gets all social statuses.
        /// </summary>
        /// <returns></returns>
        public IEnumerable<ILookUpEntity> GetAllSocialStatuses()
        {
            return _loanRepository.FindSocialStatuses(s => true);
        }

        /// <summary>
        /// Gets all educational levels.
        /// </summary>
        /// <returns></returns>
        public IEnumerable<ILookUpEntity> GetAllEducationalLevels()
        {
            return _loanRepository.FindEducationalLevels(e => true);
        }

        /// <summary>
        /// Gets all projects.
        /// </summary>
        /// <returns></returns>
        public IEnumerable<IProject> GetAllProjects()
        {
            return _adminstrationRepository.FindProjects(p => true);
        }

        /// <summary>
        /// Gets the ALL project stages.
        /// </summary>
        /// <param name="projectId">The project ID.</param>
        /// <returns></returns>
        public IEnumerable<IProjectStage> GetAllProjectStages(Guid projectId)
        {
            IProject tempProject = _adminstrationRepository.GetProjectById(projectId);
            return tempProject.Stages;
        }

        /// <summary>
        /// Gets all loan places.
        /// </summary>
        /// <returns></returns>
        public IEnumerable<ILookUpEntity> GetAllLoanPlaces()
        {
            return _loanRepository.FindLoanPlaces(lp => true);
        }

        /// <summary>
        /// Gets all legal forms.
        /// </summary>
        /// <returns></returns>
        public IEnumerable<ILookUpEntity> GetAllLegalForms()
        {
            return _loanRepository.FindLegalForms(lf => true);
        }

        /// <summary>
        /// Gets all loan types.
        /// </summary>
        /// <returns></returns>
        public IEnumerable<ILookUpEntity> GetAllLoanTypes()
        {
            return _loanRepository.FindLoanTypes(lt => true);
        }

        /// <summary>
        /// Gets all loan payment methods.
        /// </summary>
        /// <returns></returns>
        public IEnumerable<ILoanPaymentMethod> GetAllLoanPaymentMethods()
        {
            return _loanRepository.FindLoanPaymentMethods(lpm => true);
        }

        /// <summary>
        /// Gets the ALL military status.
        /// </summary>
        /// <returns></returns>
        public IEnumerable<ILookUpEntity> GetAllMilitaryStatus()
        {
            return _loanRepository.FindMilitaryStatus(ms => true);
        }

        /// <summary>
        /// Gets all centers.
        /// </summary>
        /// <returns></returns>
        public IEnumerable<ICenter> GetAllCenters()
        {
            return _adminstrationRepository.FindCenters(c => true);
        }

        #endregion

         #region [ Methods ]

        /// <summary>
        /// Gets the customer by customerID.
        /// </summary>
        /// <param name="customerId">The customer ID.</param>
        /// <returns></returns>
        public ICustomer GetCustomerById(Guid customerId)
        {
            return _loanRepository.GetCustomerByID(customerId);
        }

        /// <summary>
        /// Gets the loan by ID.
        /// </summary>
        /// <param name="loanId">The loan ID.</param>
        /// <returns></returns>
        public ICustomerLoan GetLoanById(Guid loanId)
        {
            return _loanRepository.GetCustomerLoanByID(loanId);
        }

        /// <summary>
        /// Gets the project stage by projectstageID.
        /// </summary>
        /// <param name="projectStageId">The projectstage ID.</param>
        /// <returns></returns>
        public IProjectStage GetProjectStageById(Guid projectStageId)
        {
            return _adminstrationRepository.GetProjectStageByID(projectStageId);
        }

        /// <summary>
        /// Gets center's issue places.
        /// </summary>
        /// <param name="centerId">The center ID.</param>
        /// <returns></returns>
        public IEnumerable<IIssuePlace> GetCenterIssuePlaces(Guid centerId)
        {
            return _adminstrationRepository.GetCenterByID(centerId).IssuePlaces;
        }

        /// <summary>
        /// Gets the governorate's centers.
        /// </summary>
        /// <param name="governorateId">The governorate ID.</param>
        /// <returns></returns>
        public IEnumerable<ICenter> GetGovernorateCenters(Guid governorateId)
        {
            return _loanRepository.GetGovernateByID(governorateId).Centers;
        }

        /// <summary>
        /// Creates the customer and loan.
        /// </summary>
        /// <param name="customerName">Name of the customer.</param>
        /// <param name="birthDate">The birth date.</param>
        /// <param name="gender">The gender.</param>
        /// <param name="ssid">The SSID.</param>
        /// <param name="ssidCreationDate">The ssid creation date.</param>
        /// <param name="creationLocationId">The creation location ID.</param>
        /// <param name="birthLocationId">The birth location ID.</param>
        /// <param name="cityId">The city ID.</param>
        /// <param name="address">The address.</param>
        /// <param name="phoneNumber">the customer Phone Number</param>
        /// <param name="mobileNumber">The customer Phone Number</param>
        /// <param name="relationToCommitteeMember">The relation to committee member.</param>
        /// <param name="currentJob">The current job.</param>
        /// <param name="militaryStatusId">The military status ID.</param>
        /// <param name="militaryPostpondingDate">The military postponding date.</param>
        /// <param name="socialStatusId">The social status ID.</param>
        /// <param name="educationalLevelId">The educational level ID.</param>
        /// <param name="projectStageId">The project stage ID.</param>
        /// <param name="committeeId">The committee ID.</param>
        /// <param name="loanName">Name of the loan.</param>
        /// <param name="loanAddress">The loan address.</param>
        /// <param name="loanPlaceTypeId">The loan place type ID.</param>
        /// <param name="loanAmount">The loan amount.</param>
        /// <param name="loanPaymentPeriod">The loan payment period.</param>
        /// <param name="familyCurrentDebts">The family current debts.</param>
        /// <param name="legalFormId">The legal form ID.</param>
        /// <param name="loanTypeId">The loan type ID.</param>
        /// <param name="relationWithManagers">The relation with managers.</param>
        /// <param name="recipteNumber">The recipte number.</param>
        /// <param name="loanPaymentMethodId">The loan payment method ID.</param>
        /// <param name="projectExistance">if set to <c>true</c> [project existance].</param>
        /// <param name="loanId">The loan ID.</param>
        /// <param name="customerId">The customer ID.</param>
        /// <returns></returns>
        public ICustomerLoan CreateCustomerAndLoan(string customerName, DateTime birthDate, CustomerGender gender,
                                                   string ssid, DateTime ssidCreationDate, Guid creationLocationId,
                                                   Guid birthLocationId, Guid cityId, string address, string phoneNumber,
                                                   string mobileNumber, string relationToCommitteeMember,
                                                   string currentJob, Guid militaryStatusId,
                                                   DateTime? militaryPostpondingDate, Guid socialStatusId,
                                                   Guid educationalLevelId,
                                                   Guid projectStageId, Guid committeeId, string loanName,
                                                   string loanAddress, Guid loanPlaceTypeId, double loanAmount,
                                                   int loanPaymentPeriod, string familyCurrentDebts, Guid legalFormId,
                                                   Guid loanTypeId,
                                                   string relationWithManagers, string recipteNumber,
                                                   Guid loanPaymentMethodId, bool projectExistance, string loanId,
                                                   string customerId)
        {
            using (var transaction = ServiceLocator.Current.GetInstance<IUnitOfWork>())
            {
                ICustomer customer;
                ICustomerLoan customerLoan;

                if (!loanId.IsNullOrEmpty())
                {
                    customerLoan = _loanRepository.GetCustomerLoanByID(new Guid(loanId));
                    customer = customerLoan.CustomerObj;
                }
                else
                {
                    customerLoan = this.GetInstance<ICustomerLoan>();

                    customerLoan.LoanID = Guid.NewGuid();
                    //Guid projectId = _adminstrationRepository.GetProjectStageByID(projectStageId).ProjectID;
                    customerLoan.ResponsibleID = _responsibleService.GetFreeResponsibleID(committeeId, _adminstrationRepository.GetProjectStageByID(projectStageId).ProjectObj);
                    if (!customerId.IsNullOrEmpty())
                    {
                        customer = _loanRepository.GetCustomerByID(new Guid(customerId));
                    }
                    else
                    {
                        customer = this.GetInstance<ICustomer>();
                        customer.CustomerID = Guid.NewGuid();
                    }
                }

                customer.CustomerName = customerName;
                customer.Birthdate = birthDate;
                customer.Gender = gender;
                customer.SSN = ssid;
                customer.SSNCreationDate = ssidCreationDate;
                customer.SSNCreationLocationID = creationLocationId;
                customer.BirthLocationID = birthLocationId;
                customer.Address = address;
                customer.CurrentJob = currentJob;
                customer.MilitaryStatusID = militaryStatusId;
                customer.MilitaryPostpondingDate = militaryPostpondingDate;
                customer.SocialStatusID = socialStatusId;
                customer.EducationalLevelID = educationalLevelId;
                var phone = ServiceLocator.Current.GetInstance<IPhone>();
                phone.PhoneID = Guid.NewGuid();
                phone.PhoneNumber = phoneNumber;
                phone.PhoneType = (byte)PhoneType.Phone;
                customer.AddPhone(phone);
                var mobile = ServiceLocator.Current.GetInstance<IPhone>();
                mobile.PhoneID = Guid.NewGuid();
                mobile.PhoneNumber = mobileNumber;
                mobile.PhoneType = (byte)PhoneType.Mobile;
                customer.AddPhone(mobile);

                //customer loan
                customerLoan.CustomerID = customer.CustomerID;
                customerLoan.ProjectStageID = projectStageId;
                customerLoan.CommitteeID = committeeId;
                customerLoan.LoanName = loanName;
                customerLoan.LoanAddress = loanAddress;
                customerLoan.LoanPlaceTypeID = loanPlaceTypeId;
                customerLoan.LoanAmount = loanAmount;
                customerLoan.LoanPaymentPeriod = loanPaymentPeriod;
                customerLoan.FamilyCurrentDebts = familyCurrentDebts;
                customerLoan.LegalFormID = legalFormId;
                customerLoan.LoanCreatedDate = DateTime.Now;
                customerLoan.LoanTypeID = loanTypeId;
                customerLoan.RelationWithManagers = relationWithManagers;
                customerLoan.LoanPaymentMethodID = loanPaymentMethodId;
                customerLoan.IsLoanProjectExist = projectExistance;

                if (loanId.IsNullOrEmpty())
                {
                    customerLoan.Receipt = GetNewLoanReceipt(recipteNumber);

                    if (customerId.IsNullOrEmpty())
                        _loanRepository.CreateCustomer(customer);

                    customerLoan.LoanNumber = _loanRepository.GetLastLoanNumber() + 1;
                    _loanRepository.CreateCustomerLoan(customerLoan);
                }
                if (IsProcecuted(customer.CustomerID) || IsAgeExceeded())
                {
                    
                    _workFlowService.CreateProcessInstance(Process.LoanCycle, customerLoan.LoanID, RegistryInfo.UserID,
                                                           "");
                    transaction.Commit();
                    Guid processInstId = _workFlowService.GetProcessInstanceIDByEntity(customerLoan.LoanID);
                    _workFlowService.Move(ActionTypes.RefusedFromBegining, processInstId, RegistryInfo.UserID,
                                          string.Empty, null);
                }
                transaction.Commit();
                return customerLoan;
            }
        }

        /// <summary>
        /// Determines whether the specified customer id is procecuted.
        /// </summary>
        /// <param name="customerId">The customer id.</param>
        /// <returns>
        ///   <c>true</c> if the specified customer id is procecuted; otherwise, <c>false</c>.
        /// </returns>
        private bool IsProcecuted(Guid customerId)
        {
            bool hasTrials = false;
            IEnumerable<Guid> priviousTrialsCloseTypesIDs =
                _lookupRepository.GetPreviousTrialsCloseType().Select(lookup => lookup.ID);
            ICustomer customer = _loanRepository.GetCustomerByID(customerId);
            if (customer != null)
            {
                hasTrials = (from loan in _loanRepository.FindCustomerLoans(c => true)
                             join loanHistory in _loanRepository.FindCustomerHistories(cH => true)
                                 on loan.LoanID equals loanHistory.LoanID
                             where
                                 loan.CustomerID == customer.CustomerID &&
                                 priviousTrialsCloseTypesIDs.Contains(loanHistory.LoanHistoryCloseTypeID.Value)
                             select loan.LoanAmount).Count() > 0;
            }
            return hasTrials;
        }

        private bool IsAgeExceeded()
        {
            return AgeExceeded;
        }

        public void setAgeExceedanceFlag(DateTime BirthDate, int AgeLimit)
        {
            if ((DateTime.Today - BirthDate).Days / 365.25 > AgeLimit)
            {
                AgeExceeded = true;
            }
        }


        /// <summary>
        /// Gets the next stage.
        /// </summary>
        /// <param name="customerId">The customer id.</param>
        /// <param name="hasOpenLoans">if set to <c>true</c> [has open loans].</param>
        /// <returns></returns>
        public IProjectStage GetNextStage(string customerId, out bool hasOpenLoans)
        {
            if (customerId.IsNullOrEmpty())
            {
                hasOpenLoans = false;
                return _adminstrationRepository.GetStageByNumber(1);
            }
            IEnumerable<ICustomerLoan> loans =
                _loanRepository.GetAllCustomerLoans(customerId.ToGuid()).OrderBy(loan => loan.LoanNumber);
            if (loans.Count() > 0)
            {
                foreach (ICustomerLoan item in loans)
                {
                    Guid processId = _workFlowService.GetProcessInstanceIDByEntity(loans.First().LoanID);
                    if (!_workFlowService.GetStep(processId).IsLastStep)
                    {
                        hasOpenLoans = true;
                        return null;
                    }
                }
                hasOpenLoans = false;
                return _adminstrationRepository.GetStageByNumber(loans.Last().ProjectStage.Stage + 1);
            }
            hasOpenLoans = false;
            return _adminstrationRepository.GetStageByNumber(loans.Last().ProjectStage.Stage + 1);
        }

        /// <summary>
        /// Gets the new loan receipt.
        /// </summary>
        /// <param name="manualReceiptNumber">The manual receipt number.</param>
        /// <returns></returns>
        private IReceipt GetNewLoanReceipt(string manualReceiptNumber)
        {
            var newLoanReceiptTypeId = Enums.ToString(ReceiptTypes.NewLoanReceipt).ToGuid();

            var receipt = this.GetInstance<IReceipt>();
            receipt.ReceiptID = Guid.NewGuid();
            receipt.ReceiptAmount =
                double.Parse(_adminstrationRepository.GetSettingValue(RegistryInfo.CommitteeID,
                                                                      SettingKeys.NewLoanReceiptAmount));
            receipt.ReceiptDate = DateTime.Now;
            receipt.ReceiptTypeID = newLoanReceiptTypeId;

            long seed =
                int.Parse(_adminstrationRepository.GetSettingValue(RegistryInfo.CommitteeID, SettingKeys.ReceiptSeed));
            if (seed == -1)
            {
                receipt.ReceiptNumber = long.Parse(manualReceiptNumber);
            }
            else
            {
                IEnumerable<IReceipt> receipts =
                    _loanRepository.FindReceipts(r => r.ReceiptTypeID == newLoanReceiptTypeId);
                long receiptNumber = 0;
                if (receipts.Count() > 0)
                    receiptNumber = receipts.Max(r => r.ReceiptNumber);
                receipt.ReceiptNumber = receiptNumber + 1;
            }
            return receipt;
        }

        /// <summary>
        /// Gets the receipte mode.
        /// </summary>
        /// <returns></returns>
        public bool GetReceipteMode()
        {
            long seed =
                int.Parse(_adminstrationRepository.GetSettingValue(RegistryInfo.CommitteeID, SettingKeys.ReceiptSeed));
            if (seed == -1)
                return false;
            return true;
        }

        /// <summary>
        /// Determines whether [is new ssid] [the specified ssid].
        /// </summary>
        /// <param name="ssid">The ssid.</param>
        /// <returns>
        ///   <c>true</c> if [is new ssid] [the specified ssid]; otherwise, <c>false</c>.
        /// </returns>
        public bool IsNewSsid(string ssid)
        {
            return !_loanRepository.FindCustomers(cust => true).Any(cust => cust.SSN.ToLower().Equals(ssid));
        }

        /// <summary>
        /// Validates the gurantor warning.
        /// </summary>
        /// <param name="gurantSsid">The gurant ssid.</param>
        /// <returns></returns>
        public bool ValidateGurantorWarning(string gurantSsid)
        {
            return _loanExecutionService.ValidateGurantorWarning(gurantSsid);
        }
        #endregion
    }
}